package com.honkentuber;

import java.util.List;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.users.User;

public class Database {
  private static final Logger LOG = Logger.getLogger(Database.class.getName());

  protected final PersistenceManager pm;
  protected final TimingLogger timingLogger;
  protected final MyUserCache myUserCache = new MyUserCache();

  public Database(PersistenceManager pm, TimingLogger timingLogger) {
    this.pm = pm;
    this.timingLogger = timingLogger;
  }

  /**
   * @return User, or null if none deleted.
   */
  public MyUser fetchOrCreateUser(User user) {
    if (user == null) {
      return null;
    }

    List<MyUser> myUsers = fetchUsers(user.getUserId());
    if (myUsers.isEmpty()) {
      MyUser result = new MyUser(user);
      // TODO(ptucker) use makePersistentAll
      pm.makePersistent(result);
      return result;
    }

    if (myUsers.size() > 1) {
      LOG.severe("too many instances of " + user.getUserId()
          + ": " + myUsers.size());
    }

    MyUser myUser = myUsers.get(0);
    myUserCache.add(myUser);
    return myUser;
  }

  /**
   * @return User, or null if none deleted.
   */
  public MyUser fetchUser(Key key) {
    return fetchUser(key, false /* noCache */);
  }

  /**
   * @return User, or null if none deleted.
   */
  public MyUser fetchUser(Key key, boolean noCache) {
    List<MyUser> users = fetchUsers(key);
    if (users.isEmpty()) {
      return null;
    }
    if (users.size() > 1) {
      LOG.severe("too many instances of " + key + ": " + users.size());
    }
    MyUser myUser = users.get(0);
    if (!noCache) {
      myUserCache.add(myUser);
    }
    return myUser;
  }

  /**
   * @return User, or null if none deleted.
   */
  public MyUser fetchUser(String userId) {
    List<MyUser> users = fetchUsers(userId);
    if (users.isEmpty()) {
      return null;
    }
    if (users.size() > 1) {
      LOG.severe("too many instances of " + userId + ": " + users.size());
    }
    MyUser myUser = users.get(0);
    myUserCache.add(myUser);
    return myUser;
  }

  /**
   * Caller should only handle multiple users for delete purposes.
   * @return Users matching userId. Should be 1 or 0.
   */
  @SuppressWarnings("unchecked")
  protected List<MyUser> fetchUsers(Key key) {
    Query userQuery = pm.newQuery(MyUser.class);
    userQuery.setFilter("key == keyParam");
    userQuery.declareParameters(
        "com.google.appengine.api.datastore.Key keyParam");
    List<MyUser> users = (List<MyUser>) userQuery.execute(key);
    timingLogger.recordTiming("users_fetch_by_key");
    return users;
  }

  /**
   * @return Users matching userId. Should be 1 or 0.
   */
  @SuppressWarnings("unchecked")
  protected List<MyUser> fetchUsers(String userId) {
    Query userQuery = pm.newQuery(MyUser.class);
    userQuery.setFilter("userId == idParam");
    userQuery.declareParameters("java.lang.String idParam");
    List<MyUser> users = (List<MyUser>) userQuery.execute(userId);
    timingLogger.recordTiming("users_fetch_by_id");
    return users;
  }

  /**
   * @return # users deleted.
   */
  public long deleteUser(Key key) {
    Query userQuery = pm.newQuery(MyUser.class);
    userQuery.setFilter("key == keyParam");
    userQuery.declareParameters(
        "com.google.appengine.api.datastore.Key keyParam");
    long result = userQuery.deletePersistentAll(key);
    timingLogger.recordTiming("user_delete_by_key");
    myUserCache.delete(key);
    return result;
  }

  /**
   * @return # users deleted.
   */
  @SuppressWarnings("unchecked")
  public long deleteUser(String userId) {
    Query userQuery = pm.newQuery(MyUser.class);
    userQuery.setFilter("userId == idParam");
    userQuery.declareParameters("java.lang.String idParam");
    List<MyUser> users = (List<MyUser>) userQuery.execute(userId);
    for (MyUser user : users) {
      myUserCache.delete(user.getKey());
    }
    long result = userQuery.deletePersistentAll(userId);
    timingLogger.recordTiming("user_delete_by_id");
    return result;
  }

  public MyUserCache getUserCache() {
    myUserCache.loadCache(this);
    return myUserCache;
  }
}
